#include "monitor.h"

#include <linux/nl80211.h>
#include <net/if.h>
#include <netlink/attr.h>
#include <netlink/genl/genl.h>
#include <netlink/handlers.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <netlink/socket.h>


#include <unistd.h>

#include "log.h"

static int oldval = 0.f;
static monitor_callback *setter = NULL;
static FILE *log_output;

int keep_running = 1;

static int getWifiInfo_callback(struct nl_msg *msg, void *arg) {
    struct nlattr *tb[NL80211_ATTR_MAX + 1];
    struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
    struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];

    nla_parse(tb,
        NL80211_ATTR_MAX,
        genlmsg_attrdata(gnlh, 0),
        genlmsg_attrlen(gnlh, 0),
        NULL);

    if (!tb[NL80211_ATTR_STA_INFO]) {
        log_err("GET_WIFI_INFO", "sta stats missing!\n");
        return NL_SKIP;
    }

    if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
                         tb[NL80211_ATTR_STA_INFO], stats_policy)) {
        log_err("GET_WIFI_INFO", "failed to parse nested attributes!\n");
        return NL_SKIP;
    }

    if (sinfo[NL80211_STA_INFO_SIGNAL]) {
        ((Wifi*)arg)->signal = 100+(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
    }

    return NL_OK;
}

static int getWifiStatus(Netlink *nl, Wifi *w) {
    nl->result = 1;

    struct nl_msg* msg = nlmsg_alloc();

    if (!msg) {
        log_err("GET_WIFI_STATUS", "Failed to allocate netlink message.\n");
        return -2;
    }

    genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl->id, 0, NLM_F_DUMP,
            NL80211_CMD_GET_STATION, 0);

    nla_put_u32(msg, NL80211_ATTR_IFINDEX, w->ifindex);
    nl_send_auto(nl->socket, msg);
    while (nl->result > 0) { nl_recvmsgs(nl->socket, nl->cb1); }
    nlmsg_free(msg);

    return 0;
}

void monitor(const char *ifname, monitor_callback *callback, unsigned long interval) {
    setter = callback;

    Netlink nl;
    Wifi w;
    w.ifindex = if_nametoindex(ifname); // todo it’s ioctl -> change to netlink

    nl.id = initNl80211(&nl, &w, getWifiInfo_callback);
    if (nl.id < 0) {
        log_err("MONITOR", "Error initializing netlink 802.11\n");
        exit(EXIT_FAILURE);
    }


    do {
        getWifiStatus(&nl, &w);
        if (w.signal != oldval) {
            int status = setter(oldval, w.signal, &w);
            if (status > 0)
                oldval=w.signal;
        }
        sleep(interval);
    } while(keep_running);

    log_out("MONITOR", "Exiting gracefully...\n");
    nl_cb_put(nl.cb1);
    nl_close(nl.socket);
    nl_socket_free(nl.socket);
}
